package com.hc.util.snmp.impl;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.snmp4j.CommandResponder;
import org.snmp4j.CommunityTarget;
import org.snmp4j.MessageDispatcherImpl;
import org.snmp4j.PDU;
import org.snmp4j.SNMP4JSettings;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.IpAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.MultiThreadedMessageDispatcher;
import org.snmp4j.util.ThreadPool;

import com.hc.util.snmp.APIServer;
import com.hc.util.snmp.SNMPConstens;

public class SnmpInstance {

	public static final int DEFAULT_VERSION = SnmpConstants.version2c;

	public static final long DEFAULT_TIMEOUT = 3 * 1000L;

	public static final int DEFAULT_RETRY = 3;

	private static SnmpInstance instance;
	private SnmpInstance() throws IOException{
		init();
	}
	public static SnmpInstance getInstance() throws IOException{
		if(instance == null){
			instance = new SnmpInstance();
		}
		return instance;
	}
	
	private Snmp snmp = null;
	public void init() throws IOException {
		System.out.println("---->閿熸枻鎷稴NMP<----");
		ThreadPool threadPool = ThreadPool.create("Trap", 200);
		MultiThreadedMessageDispatcher dispatcher = new MultiThreadedMessageDispatcher(threadPool,
				new MessageDispatcherImpl());
		Address listenAddress = GenericAddress.parse(System.getProperty(
				"snmp4j.listenAddress", "udp:0.0.0.0/16216")); // 閿熸枻鎷烽敓鏂ゆ嫹IP閿熸枻鎷烽敓鏂ゆ嫹閿熷壙鍖℃嫹
		TransportMapping transport;
		// 閿熸枻鎷稵CP閿熸枻鎷稶DP鍗忛敓鏂ゆ嫹閿熸枻鎷峰啓閿熸枻鎷烽敓锟�
		if (listenAddress instanceof UdpAddress) {
			transport = new DefaultUdpTransportMapping(
					(UdpAddress) listenAddress);
		} else {
			transport = new DefaultTcpTransportMapping(
					(TcpAddress) listenAddress);
		}
		snmp = new Snmp(dispatcher, transport);
		snmp.getMessageDispatcher().addMessageProcessingModel(new MPv1());
		snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c());
		snmp.getMessageDispatcher().addMessageProcessingModel(new MPv3());
		USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(
				MPv3.createLocalEngineID()), 0);
		SecurityModels.getInstance().addSecurityModel(usm);
		
		//澧炲姞trap
//		snmp.addCommandResponder(new TrapReceiver());
		try {
			snmp.addCommandResponder((CommandResponder) APIServer.getTrapReceiverService());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
		
		snmp.listen();
	}

	/**
	 * 闁告瑦鍨块敓绲奛MP濞ｅ洠鍓濋敓锟�
	 * @throws IOException
	 */
	private Object sendPDU(int type, String address, String key, Object value) throws IOException {
		CommunityTarget target = createTarget4Trap(address, type);
		PDU pdu = new PDU();

		if(value!=null){
			pdu.add(createSetVariable(key, value));
		}else{
			pdu.add(new VariableBinding(new OID(key.split(SNMPConstens.SP)[1])));
		}
		
		
		pdu.setType(type);
		ResponseEvent responseEvent = snmp.send(pdu, target);
		
		PDU response = responseEvent.getResponse();// 閿熸枻鎷烽敓鏂ゆ嫹response閿熸枻鎷�
		// response PDU閿熸枻鎷烽敓鏂ゆ嫹閿燂拷
		if (response != null) {
			if (response.getErrorIndex() == PDU.noError
					&& response.getErrorStatus() == PDU.noError) {
				@SuppressWarnings("unchecked")
				Vector<VariableBinding> vbs = (Vector<VariableBinding>) response
						.getVariableBindings();
				for (VariableBinding vb : vbs) {
					if (vb.getVariable().isException()){
						continue;
					}else{
						return vb.getVariable();
					}
				}

			} else {
				return null;
			}
		}
		return null;
		
	}
	private void sendPDUs(int type, String address, Map<String, Object> oids) throws IOException {
		CommunityTarget target = createTarget4Trap(address, type);
		PDU pdu = new PDU();

		for(String key: oids.keySet()){
			Object value = oids.get(key);
			if(value!=null){
				pdu.add(createSetVariable(key, value));
			}else{
				pdu.add(new VariableBinding(new OID(key.split(SNMPConstens.SP)[1])));
			}
		}

		pdu.setType(type);

		snmp.send(pdu, target);
	}
	
	/**
	 * 闁告瑦鍨块敓绲奛MP濞ｅ洠鍓濋敓锟�
	 * @throws IOException
	 */

	public Object setPDU(String address, String oid, Object value) throws IOException {
		return sendPDU(PDU.SET, address, oid, value);
	}
	
	/**
	 * 闁告瑦鍨块敓绲奛MP濞ｅ洠鍓濋敓锟�
	 * @throws IOException
	 */

	public Object getPDU(String address, String oid) throws IOException {
		return sendPDU(PDU.GET, address, oid, null);
	}
	
	/**
	 * 闁告瑦鍨块敓绲奛MP濞ｅ洠鍓濋敓锟�
	 * @throws IOException
	 */

	public void setPDUs(String address, Map<String, Object> oids) throws IOException {
		sendPDUs(PDU.SET, address, oids);
	}
	
	/**
	 * 闁告瑦鍨块敓绲奛MP濞ｅ洠鍓濋敓锟�
	 * @throws IOException
	 */

	public void getPDUs(String address, Map<String, Object> oids) throws IOException {
		sendPDUs(PDU.GET, address, oids);
	}
	

	/**
	 * 闁告瑦鍨块敓绲奛MP濞ｅ洠鍓濋敓锟�
	 * @param address
	 * @return CommunityTarget
	 */

	public static CommunityTarget createTarget4Trap(String address, int type) {
		CommunityTarget target = new CommunityTarget();

		if(PDU.SET == type){
			target.setCommunity(new OctetString("private"));
		}else{
			target.setCommunity(new OctetString("public"));
		}
		
		target.setAddress(GenericAddress.parse(address));

		target.setVersion(DEFAULT_VERSION);

		target.setTimeout(DEFAULT_TIMEOUT); // milliseconds

		target.setRetries(DEFAULT_RETRY);

		return target;

	}
	
	private VariableBinding createSetVariable(String key, Object value) {

		String[] oidArr = key.split(SNMPConstens.SP);
		String valueType = oidArr[0];
		String oid = oidArr[1];
		if(SNMPConstens.INTEGER.equalsIgnoreCase(valueType)){
			return new VariableBinding(new OID(oid), new Integer32(Integer.valueOf(value.toString())));
		}else if(SNMPConstens.IPADDRESS.equalsIgnoreCase(valueType)){
			return new VariableBinding(new OID(oid), new IpAddress(value.toString()));
		}else {
			return new VariableBinding(new OID(oid), new OctetString(value.toString()));
		}
	}
	
	public void snmpGetBulk(Map<Integer, Map<String, Object>> datas, List<Map<String, Object>> list, OID oid,
			String address, String rootOID)
			throws IOException {
		OID curr_oid = oid;
		while(true){
			CommunityTarget target = createTarget4Trap(address, PDU.GET);
			
			PDU request = new PDU();
			request.setType(PDU.GETBULK);
			request.setMaxRepetitions(10);// 姣忔杩斿洖姊濇暩
			// request.setNonRepeaters(2);// 妯欓噺鍊嬫暩 //妯欓噺鍦ㄥ墠 鐭㈤噺鍦ㄥ緦
			request.add(new VariableBinding(curr_oid));
			ResponseEvent rspEvt = snmp.send(request, target);
			PDU response = rspEvt.getResponse();
			if (null != response && response.getErrorIndex() == PDU.noError
					&& response.getErrorStatus() == PDU.noError) {
				curr_oid = null;
				// 寰挵鏁告摎妗�
				Vector<VariableBinding> vbs = (Vector<VariableBinding>) response
						.getVariableBindings();
				for (VariableBinding variable : vbs) {
					//鎶婅鍙峰幓鎺�
					int[] oidArr = new int[variable.getOid().getValue().length - 1];
					System.arraycopy(variable.getOid().getValue(), 0, oidArr, 0, oidArr.length);
					
					String key = SNMP4JSettings.getOIDTextFormat().format(oidArr);
					int rowNum = variable.getOid().last();
					if (key.contains(rootOID)) {// 鍒ゆ柇鑾峰緱鐨勫�鏄惁鏄寚瀹氭牴鑺傜偣涓嬮潰
						Map<String, Object> tempMap;
						if(datas.containsKey(rowNum)){
							tempMap = datas.get(rowNum);
						}else{
							tempMap = new HashMap<String, Object>();
							datas.put(rowNum, tempMap);
							list.add(tempMap);
						}
						
						Object value;
						if(variable.getVariable() instanceof Integer32){
							value = variable.getVariable().toInt();
						}else{
							value = variable.getVariable().toString();
						}
						
						tempMap.put(key, value);
						System.out.println(key+"="+value);
						curr_oid = variable.getOid();
					} else {
						return;   
					}
				}
				if (null == curr_oid) {
					return;
				}
//				snmpGetBulk(datas, curr_oid, address, rootOID);
			}
		}
		
	}
}
